﻿using Location.API.Model;
using Location.API.ViewModel;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Location.API.Repositories
{
    public class LocationsRepository : ILocationsRepository
    {
        private readonly LocationsContext _context;

        public LocationsRepository(IOptions<LocationSettings> settings)
        {
            _context = new LocationsContext(settings);
        }
        public async Task<Locations> GetLocationsAsync(string locationId)
        {
            var filter = Builders<Locations>.Filter.Eq("LocationId", locationId);
            return await _context.Locations
                                 .Find(filter)
                                 .FirstOrDefaultAsync();
        }

        public async Task<IEnumerable<Locations>> GetLocationListAsync()
        {
            return await _context.Locations.Find(new BsonDocument()).ToListAsync();
        }

        public async Task AddLocationsAsync(Locations Location)
        {
            await _context.Locations.InsertOneAsync(Location);
        }

        public async Task UpdateLocationsAsync(Locations location)
        {
            await _context.Locations.ReplaceOneAsync(
                doc => doc.Id == location.Id,
                location,
                new ReplaceOptions { IsUpsert = true });
        }
        public async Task<IEnumerable<UserLocation>> GetUserLocationByUserIdAsync(string userId)
        {
            var filter = Builders<UserLocation>.Filter.Eq("UserId", userId);
            return await _context.UserLocation
                                 .Find(filter).ToListAsync();
        }
        public async Task<IEnumerable<UserLocationResponse>> GetUserLocationAsync(string userId)
        {
            var results = from a in _context.UserLocation.AsQueryable()
                          join b in _context.Locations.AsQueryable() on a.LocationId equals b.Id
                          where a.UserId == userId
                          select new UserLocationResponse
                          {
                              Id = b.Id,
                              ParentId = b.ParentId,
                              Description = b.Description,
                              Latitude = b.Latitude,
                              Longitude = b.Longitude,
                              Remark = b.Remark,
                              CreateTime = b.CreateTime,
                              IsDefault = a.IsDefault
                          };
            return await Task.FromResult(results.ToList());
        }

        public async Task AddUserLocationAsync(string userId,Locations location)
        {
           await _context.StartTransactionAsync(new Action<IClientSessionHandle>(async (session)=>
            {
                await _context.Locations.InsertOneAsync(session, location);
                UserLocation userLocation = new UserLocation(userId, location.Id);
                //用户不存在地址时，则设置为默认
                var filter = Builders<UserLocation>.Filter.Eq("UserId", userId);
                var currentLocation = await _context.UserLocation.FindAsync(filter);
                if(!currentLocation.Any())
                    userLocation.SetDefault();
                await _context.UserLocation.InsertOneAsync(session, userLocation);
            }));
        }

        public async Task UpdateUserLocationAsync(string userId, Locations location)
        {
            var userLocations = await GetUserLocationByUserIdAsync(userId);
            if(userLocations.Any(t => t.LocationId == location.Id))
            {
               await UpdateLocationsAsync(location);
            }
            else
            {
                throw new Exception($"userId:{userId} mapping Locations can not match locationId:{location.Id}");
            }
        }

        public async Task<ResidentialAreasLocation> GetResidentialAreasLocationByAreasIdAsync(string residentialAreasId)
        {
            var filter = Builders<ResidentialAreasLocation>.Filter.Eq("ResidentialAreasId", residentialAreasId);
            return await _context.ResidentialAreasLocation
                                 .Find(filter)
                                 .FirstOrDefaultAsync();
        }
        public async Task<Locations> GetResidentialAreasLocationAsync(string residentialAreasId)
        {
            var results = from a in _context.ResidentialAreasLocation.AsQueryable()
                          join b in _context.Locations.AsQueryable() on a.LocationId equals b.Id
                          where a.ResidentialAreasId == residentialAreasId
                          select b;
            return await Task.FromResult(results.FirstOrDefault());
        }
        public async Task AddResidentialAreasLocationAsync(string residentialAreasLocationId,Locations location)
        {
            await _context.StartTransactionAsync(new Action<IClientSessionHandle>(async (session) =>
            {
                await _context.Locations.InsertOneAsync(session, location);
                ResidentialAreasLocation residentialAreasLocation = new ResidentialAreasLocation(residentialAreasLocationId,location.Id);
                await _context.ResidentialAreasLocation.InsertOneAsync(session, residentialAreasLocation);
            }));
        }
        public async Task UpdateResidentialAreasLocationAsync(string residentialAreasLocationId, Locations location)
        {
            var residentialAreasLocation = await GetResidentialAreasLocationByAreasIdAsync(residentialAreasLocationId);
            if(residentialAreasLocation.LocationId == location.Id)
            {
               await UpdateLocationsAsync(location);
            }
            else
            {
                throw new Exception($"ResidentialAreasLocationId:{residentialAreasLocationId} mapping Locations can not match locationId:{location.Id}");
            }
        }
        public async Task<ExpressStoreLocation> GetExpressStoreLocationByStoreIdAsync(string expressStoreLocationId)
        {
            var filter = Builders<ExpressStoreLocation>.Filter.Eq("ExpressStoreId", expressStoreLocationId);
            return await _context.ExpressStoreLocation
                                 .Find(filter)
                                 .FirstOrDefaultAsync();
        }
        public async Task<Locations> GetExpressStoreLocationAsync(string expressStoreId)
        {
            var results = from a in _context.ExpressStoreLocation.AsQueryable()
                          join b in _context.Locations.AsQueryable() on a.LocationId equals b.Id
                          where a.ExpressStoreId == expressStoreId
                          select b;
            return await Task.FromResult(results.FirstOrDefault());
        }

        public async Task AddExpressStoreLocationAsync(string expressStoreId, Locations location)
        {
            await _context.StartTransactionAsync(new Action<IClientSessionHandle>(async (session) =>
            {
                await _context.Locations.InsertOneAsync(session, location);
                ExpressStoreLocation expressStoreLocation = new ExpressStoreLocation(expressStoreId, location.Id);
                await _context.ExpressStoreLocation.InsertOneAsync(session, expressStoreLocation);
            }));
        }
        public async Task UpdateExpressStoreLocationAsync(string expressStoreId, Locations location)
        {
            var expressStoreLocation = await GetExpressStoreLocationByStoreIdAsync(expressStoreId);
            if(expressStoreLocation.LocationId == location.Id)
            {
                await UpdateLocationsAsync(location);
            }
            else
            {
                throw new Exception($"ExpressStoreId:{expressStoreId} mapping ExpressStoreLocation can not match locationId:{location.Id}");
            }
        }
    }
}
